iT邦幫忙

2023 iThome 鐵人賽

DAY 5
0

Table of Contents

  • for-in
  • 好像有跟for in很像的for of
  • 怎麽做才能讓for-of用在物件上?
  • References

在昨天的文章中,我們探討幾種允許我們分別訪問物件key/value的方法,同時還能夠利用返回的陣列執行其他操作。

for-in

for-in總該是遍歷了吧?來看看MDN的說明:

The for...in statement iterates over all enumerable string properties of an object (ignoring properties keyed by symbols), including inherited enumerable properties.

上文重點:1. 可以迭代 2.只用在可列舉的屬性物件

for-in可以用於迭代物件中的屬性,在MDN提到的基本語法如下:

for (variable in object) {
  statement
}

//示範

const contestant = {
  contestantId: 1,
  contestantName: "Alice",
  hotpotFlavor: "Spicy Sichuan",
  hotpotIngredients: ["Beef slices", "Tofu", "Enoki mushrooms", "Napa cabbage"],
  summarizeCooking: function () {
    return "Balancing spicy Sichuan flavor with tender beef, tofu, and crunchy cabbage. Pairing with fragrant jasmine tea enhances the experience.";
  },
}

for(const key in contestant){
  console.log(key);
    // 印出:
    // contestantId
    // contestantName
    // hotpotFlavor
    // hotpotIngredients
    // summarizeCooking
}

那麽,對於變數可以使用哪些宣告關鍵字?varletconst都可以,或者在=之前的宣告也有效,例如:

let key

for(key in contestant){
  if (key === 'contestantId'){
    continue;
  }
  console.log(key);
}

使用const宣告之後一定要賦值,如果需要使用const來宣告變數,就避免使用先宣告後再賦值的方式。此外可以使用continuebreak跳過指定項目或中止循環。

好像有跟for-in很像的for-of

在上面介紹for-in的用法,會發現在MDN的列表上緊接介紹for-of的使用,讓我們再看看for-of的基本語法:

for (variable of iterable)
  statement

從語法來看好像沒有太大區別,但會看到of後面有個iterable,先不論這是什麽,我們試著讓物件應用在這個語法中:

for(const key of contestant){
  console.log(key);
}//TypeError: contestant is not iterable

出錯了!回過頭來先看一下MDN如何寫for-of可以使用哪些內容來遍歷:

The for-of statement executes a loop that operates on a sequence of values sourced from an iterable object.

因此,for-of是用在可以被迭代的物件上面。
看到這大概會有疑問:

  1. 什麽是可迭代物件(iterable object)?
  2. 什麽叫可迭代(iterable)?
  3. 我們現在所使用的object跟上面所說的不一樣嗎?

讓我們從以下開始解釋。

怎麽做才能讓for-of用在物件上?

這個部分將涉及到迭代的相關知識,由於時間有限且已有人撰寫了詳細的解釋,因此在這裡我們將僅概述原因和解決方法,並列出前因後果。我們只專注在如何對物件使用for-of的方式。

前因

  1. 為了避免混淆,下文中使用{}new建立的物件先稱作Ordinary Object

  2. for-in是ES5的語法,是能遍歷鍵(key)的做法,ES6引入迭代(iteration)的遍歷數據的概念,以及for-of來遍歷value的語法,兩者搭配使用。

  3. 迭代使用的是名為可迭代(iterable)數據結構,使elements(元素)可以被訪問,並使用迭代器(iterator)來遍歷。

  4. 迭代涉及迭代協議(Iteration protocols)該協議定義可迭代跟迭代器的行為,特色是具有順序性,使迭代器一次只回傳一個值。迭代協議分別為可迭代協議(iterable protocol)與迭代器協議(iterator protocol)。

  5. 可迭代協議允許物件定義或自定義迭代行為,像是定義在for-of中可以迭代的對象,可以進行迭代行為的型別有:Arrays、Strings、Maps、Sets、DOM data structures,請記得Ordinary Object不包含在內--,不然也不會有這段說明了--。要使Ordinary Object成為可迭代的,需要實踐@@iterator方法,並透過Symbol.iterator讓物件獲得@@iterator關鍵字的屬性

  • Symbol.iterator:是沒有參數的函式,回傳一個符合迭代器協議的、沒有參數物件。

(再來看別人針對Ordinary Object為何不是iterable的想法,我只能猜想在Javascript中被允許能夠迭代的型別是有順序性的,但Ordinary Object並沒有這種順序性,最多只是可列舉的(Enumerable),所以我們想讓Ordinary Object能夠被迭代,我們需要採取特定的方法,讓它具備順序性。)

  1. 迭代器協議定義迭代器與產生值的方法,只要一個物件具有next()方法時,就會被視為一個迭代器,且迭代器是可以生成有限或無限的值,如果沒有設置一個中止條件,可能會無限生成。

也就是說,其他物件已經內建Symbol.iterator()這個方法,需要迭代的時候可以直接拿來使用。
但是如果希望Ordinary Object可以被for-of使用,就需要在物件中設定Symbol.iterator()next()來設定回傳的方式。

整理說明

如何迭代物件

如果想要自動迭代所有值,就使用for-of
如果想要手動迭代,就要使用自訂或內建的[Symbol.iterator](),並使用next()逐個印出,印出的值會有兩個:done跟value。

以下使用Array示範:

const array = [1,2,3]
const log = array[Symbol.iterator]()

console.log(log.next());//{ value: 1, done: false }
console.log(log.next());//{ value: 2, done: false }
console.log(log.next());//{ value: 3, done: false }
console.log(log.next());//{ value: undefined, done: true }

value為物件的值,done則為布林值,false指的是尚未迭代結束,true則是完成,但就會發現需要把所有值都印出之後,下一輪才會真的顯示結束。

這次讓Ordinary Object加上方法,結合Object.keys()進行遍歷:

const contestant = {
  contestantId: 1,
  contestantName: "Alice",
  hotpotFlavor: "Spicy Sichuan",
  hotpotIngredients: ["Beef slices", "Tofu", "Enoki mushrooms", "Napa cabbage"],
  summarizeCooking: function () {
    return "Balancing spicy Sichuan flavor with tender beef, tofu, and crunchy cabbage. Pairing with fragrant jasmine tea enhances the experience.";
  },
  //回傳一個符合迭代器協議的物件,也就是這個物件本身
  [Symbol.iterator]() {
      this.index = 0;
      return this
  },
  //針對上面的方法設置印出的內容
  next(){
    //如果沒有設置中止條件就會一直印出結果,故使用判斷式判斷中止條件
    if (this.index<Object.keys(this).length){
      //keys = //['contestantId','contestantName','hotpotFlavor','hotpotIngredients','summarizeCooking','next','index']
      const keys = Object.keys(this);
      //key = keys[0]、keys[1]...
      const key = keys[`${this.index}`]
      //index = 1、2...
      this.index++
      //value = contestant的key[0]、key[1]...
      return {done:false,value:this[key]}
    } else {
      return {done:true};
    }
  }
}

for(const content of contestant){
  console.log(content);
}

// 印出結果:
// 1
// Alice
// Spicy Sichuan
// [ 'Beef slices', 'Tofu', 'Enoki mushrooms', 'Napa cabbage' ]
// [Function: summarizeCooking]
// [Function: next]
// 7

什麽時候用哪一種遍歷?

在這個問題之前,我們再來看某個東西:

//使用最上面的範例
const contestant = {
  contestantId: 1,
  contestantName: "Alice",
  hotpotFlavor: "Spicy Sichuan",
  hotpotIngredients: ["Beef slices", "Tofu", "Enoki mushrooms", "Napa cabbage"],
  summarizeCooking: function () {
    return "Balancing spicy Sichuan flavor with tender beef, tofu, and crunchy cabbage. Pairing with fragrant jasmine tea enhances the experience.";
  },
}
//對這個物件設置原型(注意:此程式碼只是範例,不建議在原有的物件上額外增加原型,可參考MDN對Object.setPrototypeOf()的說明)
const addNewItem = { fruit:'apple',};
Object.setPrototypeOf(contestant,addNewItem);

//新增原型之後
console.log(contestant);
//印出:
//{
//   contestantId: 1,
//   contestantName: 'Alice',
//   hotpotFlavor: 'Spicy Sichuan',
//   hotpotIngredients: [ 'Beef slices', 'Tofu', 'Enoki mushrooms', 'Napa cabbage' ],
//   summarizeCooking: [Function: summarizeCooking]
// }

for(const key in contestant){
  console.log(key);
//印出:
// contestantId
// contestantName
// hotpotFlavor
// hotpotIngredients
// summarizeCooking
// fruit
}

就會發現當我們新增原型之後,for-in會連同原型的key一起印出來。因此如果有需要的話,建議使用昨天提到object的method,就會只印出名稱……但說到底,這個prototype到底是什麽?可列舉又是什麽?

之後再揭曉。

References

Why are Objects not Iterable in JavaScript?
JavaScript iterators and generators: A complete guide
認識 JavaScript Iterable 和 Iterator
21. Iterables and iterators
Iterator 和 for...of 循环
迭代器(Iterator)

  • MDN
  1. for...in
  2. for...of
  3. Iterators and generators
  4. Iteration protocols
  5. Symbol.iterator

上一篇
〈Day4〉看起來像是遍歷物件的Object method
下一篇
〈Day6〉原型
系列文
廚藝不精也可以,給自己做一份Javascript小火鍋30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言